﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Singer.Shared;
using Singer.Shared.UserContext;
using System.Security.Claims;

namespace Singer.Middleware.JwtAuth;

/// <summary>
/// 验证token是否有效
/// 从每次请求携带的JwtToken中解析出登录的用户信息 
/// </summary>
public class JwtAuthCheckFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        string? token = ReadToken(context).Token;
        var endpoint = context.HttpContext.Features.Get<IEndpointFeature>()?.Endpoint;
        if (endpoint == null || endpoint.Metadata.Count() == 0)
        {
            await next();
            return;
        }
        var allowAnonymousAttribute = endpoint.Metadata.Any(x => x is AllowAnonymousAttribute);
        if (allowAnonymousAttribute)
        {
            await next();
            return;
        }
        List<AuthorizeAttribute>? authorizes = endpoint?.Metadata.Where(x => x is AuthorizeAttribute).Select(x => (AuthorizeAttribute)x).ToList();
        var jwtAuth = authorizes?.FirstOrDefault(x => x != null && x.AuthenticationSchemes == JwtBearerDefaults.AuthenticationScheme);
        if (jwtAuth == null)
        {
            await next();
            return;
        }
        IJwtAuthTokenManager? tokenManager = context.HttpContext.RequestServices.GetService<IJwtAuthTokenManager>();
        if (tokenManager != null)
        {
            if (string.IsNullOrWhiteSpace(token))
                throw new CoreException("身份认证失效", ErrorCode.Auth);
            //判断缓存中是否存在
            bool existsToken = await tokenManager.ExistsTokenAsync(token);
            if (!existsToken)
                throw new CoreException("身份认证失效", ErrorCode.Auth);
        }
        await next();
    }

    /// <summary>
    /// 解析JwtToken中的用户信息，放入用户信息上下文中
    /// </summary>
    private IUserContext ReadToken(ActionExecutingContext context)
    {
        IUserContext? userContext = null;
        string ip = context.HttpContext.Connection.RemoteIpAddress?.MapToIPv4()?.ToString() ?? "";
        string? jwtToken = null;
        //解析请求中携带的JwtToken
        string? bearerToken = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault();
        if (!string.IsNullOrWhiteSpace(bearerToken) && bearerToken.StartsWith(JwtBearerDefaults.AuthenticationScheme + " "))
        {
            jwtToken = bearerToken.Split(' ')[1];
            ClaimsPrincipal? claims = TokenExtensions.ReadJwtTokenAsClaims(jwtToken);
            if(claims != null)
            {
                context.HttpContext.User = claims;
                userContext = TokenExtensions.ReadClaimsAsUserContext(claims);
            }
        }
        userContext = userContext ?? new DefaultUserContext();
        userContext.Ip = ip;
        userContext.Token = jwtToken;
        IUserContextAccessor? userContextAccessor = context.HttpContext.RequestServices.GetService<IUserContextAccessor>();
        if (userContextAccessor != null)
            userContextAccessor.UserContext = userContext;
        return userContext;
    }
}
